﻿using AiMoBridge.Interface;
using System;
using System.Drawing;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading;

namespace AiMoRhino.Pipeline
{
    class PipeServer : IDisposable
    {
        StreamWriter Writer { get; }
        StreamReader Reader { get; }
        NamedPipeServerStream Server { get; }
        public bool Connected => Server.IsConnected;
        string CurrentServerName { get; }
        PipePrinter Printer { get; }
        readonly IExecutor executor;
        readonly IBridgeInfo bridgeInfo;
        public PipeServer(IExecutor rhinoExecutor, IBridgeInfo bridgeInfo)
        {
#if DEBUG
            CurrentServerName = "AimoPipe_123456";
#else
            CurrentServerName = "AimoPipe_" + Guid.NewGuid().ToString();
#endif
            Server = new NamedPipeServerStream(CurrentServerName, PipeDirection.InOut);
            Reader = new StreamReader(Server);
            Writer = new StreamWriter(Server);
            Printer = new PipePrinter(this);
            executor = rhinoExecutor;
            this.bridgeInfo = bridgeInfo;
        }


        /// <summary>
        /// InputAimoExePath
        /// </summary>
        public void StartServer(string path)
        {
            new Thread(() => { StartServerAsync(path); }).Start();
        }

        bool InRunningCommandFromAimo { get; set; } = false;

        void StartServerAsync(string path)
        {
#if DEBUG
            Rhino.RhinoApp.WriteLine($"Debug: AiMo Start Server Pipeline at {DateTime.Now}");
#else
            System.Diagnostics.Process process = System.Diagnostics.Process.Start(path, CurrentServerName);
            if (process == null)
            {
                Eto.Forms.MessageBox.Show("Cannot Start Aimo [0090]");
                return;
            }
#endif
            try
            {
                Server.WaitForConnection();
#if DEBUG
                Rhino.RhinoApp.WriteLine($"Debug: AiMo Server Connected Done at {DateTime.Now}");
#endif
                while (true)
                {
                    string data = Reader.ReadLine();
                    {
                        if (data == null)
                        {
                            //当对方退出后，会回复null
                            break;
                        }
                        else if (data == "Connected")
                        {
                            string content = Join(bridgeInfo.CurrentRhinoVersion, Join(bridgeInfo.RhinoAssemblyLocations));
                            SendOneLine(content);
                        }
                        else if (data.StartsWith("ScreenShot-"))
                        {
                            string[] values = data.Split('-');
                            int width = int.Parse(values[1]);
                            int height = int.Parse(values[2]);
                            SendScreenShotBitmap(width, height);
                        }
                        else if (data.StartsWith("Run-"))
                        {
                            //从AiMo过来，发给Rhino
                            InRunningCommandFromAimo = true;
                            IExecutorInfo executorInfo = new ExecutorInfo(Split(data.Substring(7)), data.StartsWith("Run-CN-"), Printer);
                            bool runResult = executor.RunSriptCommand(executorInfo);
                            Printer.SendRest();
                            SendOneLine(Join(PipeUtils.EndFlag, runResult.ToString(), executor.RunCommandTime, executor.RunCommandError));
                            InRunningCommandFromAimo = false;
                        }
                        else if (data.Contains("Test Connected"))
                        {
                            SendOneLine("Received");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (!m_Disposed)
                {
                    Eto.Forms.MessageBox.Show($"AiMo external pipeline disconnected unexpected: \r\n   {ex.Message}\r\n    {ex.StackTrace}");
                    Dispose();
                }
            }
        }

        public void SendDebugText(string text)
        {
            if (InRunningCommandFromAimo)
                SendOneLine(PipeUtils.PrintFlag + Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(text)));
        }

        void SendScreenShotBitmap(int width, int height)
        {
            Size size = new Size(width, height);
            string content = "";
            try
            {
                Bitmap bitmap = bridgeInfo.GetScreenShot(size);

                if (bitmap != null)
                {
                    MemoryStream stream = new MemoryStream();
                    bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
                    byte[] imgBytes = stream.GetBuffer();
                    content = Convert.ToBase64String(imgBytes);
                    stream.Close();
                    stream.Dispose();
                }
            }
            catch { }
            SendOneLine(content);
        }

        void SendOneLine(string content)
        {
            Writer.WriteLine(content);
            Server.WaitForPipeDrain();
            Writer.Flush();
        }
        string[] Split(string contents)
        {
            return contents.Split(new string[] { PipeUtils.Spliter }, StringSplitOptions.None).Select(x => x.Replace(PipeUtils.ReturnFlag, "\r\n")).ToArray();
        }
        string Join(params string[] separator)
        {
            return string.Join(PipeUtils.Spliter, separator.Select(x => x.Replace("\r\n", PipeUtils.ReturnFlag)));
        }

        bool m_Disposed = false;
        public void Dispose()
        {
            try
            {
                Server.Close();
                Server.Dispose();
                Reader.Close();
                Reader.Dispose();
                Writer.Close();
                Writer.Dispose();
                m_Disposed = true;
            }
            catch { }
        }
    }
}
